import 'dart:ffi' as ffi;
import 'dart:io';
import 'package:ffi/ffi.dart' as ffi_pkg;

typedef _GetCurrentEnvNative = ffi.Pointer<ffi.Void> Function();
typedef _GetCurrentEnvDart = ffi.Pointer<ffi.Void> Function();

// TSFN sync exports
typedef _MathRandomSyncNative = ffi.Int32 Function(ffi.Pointer<ffi.Double> out);
typedef _MathRandomSyncDart = int Function(ffi.Pointer<ffi.Double> out);
typedef _MathRandomSyncNF = ffi.NativeFunction<_MathRandomSyncNative>;

typedef _StringValueOfSyncNative = ffi.Int32 Function(
  ffi.Int32 n,
  ffi.Pointer<ffi.Char> buf,
  ffi.Size bufsize,
  ffi.Pointer<ffi.Size> outLen,
);
typedef _StringValueOfSyncDart = int Function(
  int n,
  ffi.Pointer<ffi.Char> buf,
  int bufsize,
  ffi.Pointer<ffi.Size> outLen,
);
typedef _StringValueOfSyncNF = ffi.NativeFunction<_StringValueOfSyncNative>;

class NapiBridgeLib {
  NapiBridgeLib._(this._dl)
      : _getEnv = _dl
            .lookupFunction<_GetCurrentEnvNative, _GetCurrentEnvDart>(
                'dartnapi_get_current_env'),
        _mathRandomSync = (() {
          try {
            return _dl.lookupFunction<_MathRandomSyncNative, _MathRandomSyncDart>(
                'dartnapi_math_random_sync');
          } catch (_) {
            return null;
          }
        })(),
        _stringValueOfSync = (() {
          try {
            return _dl.lookupFunction<_StringValueOfSyncNative, _StringValueOfSyncDart>(
                'dartnapi_string_valueof_sync');
          } catch (_) {
            return null;
          }
        })();

  final ffi.DynamicLibrary _dl;
  final _GetCurrentEnvDart _getEnv;
  final _MathRandomSyncDart? _mathRandomSync;
  final _StringValueOfSyncDart? _stringValueOfSync;

  static NapiBridgeLib load({String baseName = 'dartnapi'}) {
    // This is the plugin-provided bridge .so (built from src/)
    if (Platform.isWindows) {
      return NapiBridgeLib._(ffi.DynamicLibrary.open('$baseName.dll'));
    }
    if (Platform.isMacOS || Platform.isIOS) {
      return NapiBridgeLib._(
          ffi.DynamicLibrary.open('$baseName.framework/$baseName'));
    }
    return NapiBridgeLib._(ffi.DynamicLibrary.open('lib$baseName.so'));
  }

  ffi.Pointer<ffi.Void> getCurrentEnv() => _getEnv();

  // Returns null on failure or if symbol missing
  double? mathRandomSync() {
    final fn = _mathRandomSync;
    if (fn == null) return null;
    final p = ffi_pkg.calloc<ffi.Double>();
    try {
      final rc = fn(p);
      if (rc != 0) return null;
      return p.value;
    } finally {
      ffi_pkg.calloc.free(p);
    }
  }

  // Returns null on failure or if symbol missing
  String? stringValueOfSync(int n) {
    final fn = _stringValueOfSync;
    if (fn == null) return null;
    final arena = ffi_pkg.Arena();
    try {
      final outLen = arena<ffi.Size>();
      // Probe length
      fn(n, ffi.Pointer<ffi.Char>.fromAddress(0), 0, outLen);
      final len = outLen.value;
      final buf = arena<ffi.Char>(len + 1);
      final rc = fn(n, buf, len + 1, outLen);
      if (rc != 0) return null;
      final bytes = buf.cast<ffi.Uint8>().asTypedList(len);
      return String.fromCharCodes(bytes);
    } finally {
      arena.releaseAll();
    }
  }
}

 




